#ifndef MAIN_HAL_UTILS_TraceAndroidProfile_H_
#define MAIN_HAL_UTILS_TraceAndroidProfile_H_

#include <stdio.h>

#include <iostream>
#include <stdio.h>
#include <stdarg.h>
#include <chrono>

#define MAX_FD 10

#define TraceAndroidProfile_B(...) \
    TraceAndroidProfile TraceAndroidProfile; \
    TraceAndroidProfile.begin(LOG_TAG, __func__, ##__VA_ARGS__);

struct read_format {
    uint64_t nr;
    struct {
        uint64_t value;
        uint64_t id;
    } values[];
};

int create_hardware_perf(int grp_fd, enum perf_hw_id hw_ids, uint64_t *ioc_id)
{
    if(PERF_COUNT_HW_MAX <= hw_ids || hw_ids < 0) {
        printf("Unsupport enum perf_hw_id.\n");
        return -1;
    }
    
    struct perf_event_attr pea;
    
    memset(&pea, 0, sizeof(struct perf_event_attr));
    pea.type = PERF_TYPE_HARDWARE;
    pea.size = sizeof(struct perf_event_attr);
    pea.config = hw_ids;
    pea.disabled = 1;
    pea.exclude_kernel = 1;
    pea.exclude_hv = 1;
    pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
    int fd = syscall(__NR_perf_event_open, &pea, 0, -1, grp_fd>2?grp_fd:-1, 0);
    if (fd == -1) {
        fprintf(stderr, "Error opening leader %llx\n",pea.config);
        return -1;
    }   
    ioctl(fd, PERF_EVENT_IOC_ID, ioc_id);
    return fd;
}

// help: adb shell sysctl -w kernel.perf_cpu_time_max_percent=0
class TraceAndroidProfile{
 private:
    int mFd[MAX_FD] = {-1};
    uint64_t mId[MAX_FD] = {0};
    uint64_t mValue[MAX_FD] = {0};
 public:
    bool mLogOn = true;
    char mTag[48]={0};
    double st;  //start time
    TraceAndroidProfile() {
    }

    ~TraceAndroidProfile() {
        if (mLogOn) {
            uint8_t buf[4096];
            struct read_format* rf = (struct read_format*) buf;
            ioctl(mFd[0], PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
            int len = read(mFd[0], buf, sizeof(buf));
            for (int i = 0; i < rf->nr; i++) {
                if (rf->values[i].id == mId[0]) {
                    mValue[0] = rf->values[i].value;
                } else if (rf->values[i].id == mId[1]) {
                    mValue[1] = rf->values[i].value;
                } else if (rf->values[i].id == mId[2]) {
                    mValue[2] = rf->values[i].value;
                }
            }
            LOGI("cpu cycles: \t%lu ", mValue[0]);
            LOGI("inst cnt: \t%lu ", mValue[1]);
            LOGI("cache misses: \t%lu", mValue[2]);

            close(mFd[0]);
            close(mFd[1]);
            close(mFd[2]);
        }
    }

    bool begin(const char *logtagstr, const char *func_name) {
        begin(logtagstr, func_name, "");
        st = get_sys_secend();
        return true;
    }

    bool begin(const char *logtagstr, const char *func_name, const char *format, ...){
        if (mLogOn) {
            // PERF_COUNT_HW_INSTRUCTIONS, PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_CACHE_MISSES
            mFd[0] = create_hardware_perf(-1, PERF_COUNT_HW_CPU_CYCLES, &mId[0]);
            mFd[1] = create_hardware_perf(mFd[0], PERF_COUNT_HW_INSTRUCTIONS, &mId[1]);
            mFd[2] = create_hardware_perf(mFd[0], PERF_COUNT_HW_CACHE_MISSES, &mId[2]);

            ioctl(mFd[0], PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
            ioctl(mFd[0], PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);

            va_list args;
            va_start(args, format);
            int len = snprintf(mTag, sizeof(mTag), "%s %s() ", logtagstr, func_name);
            vsnprintf(mTag + len, sizeof(mTag) - len, format, args);
            va_end(args);
            log_tag("\033[0;32m", "I", "", "[T_BEG] %s E", mTag);
        } else {
            return false;
        }

        return true;
    }
};

#endif /* MAIN_HAL_UTILS_TraceAndroidProfile_H_ */
